﻿//documentation:
/*
__Arrow properties and how they change the shooting.__
frame when the arrow comes into existance, is when it will shoot.
rotation of the arrow changes the direction of the shot.
length of arrow changes the velocity of the shot.
width of the arrow changes the random spread (inaccuracy) of the shot.
instance name of arrow tells it what bullet linkage class name to spawn for the shot.

__notes__
rotation is attempted to be global, accepting rotations and flip's up the display tree to alter the direction correctly.
-might not yet cover every possibility, havn't tested it in a rotated world either.

for bullet specific properties, such as collisions and gravity see bullet class.

developed for use with wck by SketchBookGames
box2dFlash (2.01 or 2.1) versions should be somewhat easy conversion(s) if requested


not required to be on a BodyShape.
is required to be on World (within [worldFathoms] levels of the world up the display tree, or you can change worldFathoms);
bullet names required not to contain the String "instance" since that is an identifying factor that the name was left out and to use "bullet" as default.

"_"'s can be use in instance names of shoot arrows, and will not be reflected in the bullet name.  so you cannot use _ in the linkage name of a bullet.
this became necissary due to some library conflict of having the grenade shoot flames. perhaps flash is just confused.  but a good feature anyway i hope.
*/
package extras.sketchbookgames.tools{
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.events.Event;
	import wck.World;
	import wck.BodyShape;
	import flash.geom.Point;
	import flash.utils.getDefinitionByName;
	import flash.geom.Matrix;
	import fl.motion.MatrixTransformer;
	
	//import extras.sketchbookgames.props.*
	
	public class shootArrow extends MovieClip{
		
		//OPTIONS
		public var addBodyVelocity:Boolean = false;//good in some situations.
		public var refireOnContinuedExistance:Boolean = true;
		//end OPTIONS
		
		
		private var world:wck.World;
		private var body:BodyShape;
		private var pos:Point = new Point();
		private var angle:Point = new Point();//unit vector
		private var _rotationToWorld:Number = 0;
		private var spawnDepth:int;
		
		private var worldFathoms:int = 20
		var bulletName:String;
		
		public function shootArrow(){
			visible=false;
			addEventListener(Event.ADDED_TO_STAGE, AddedToStage);
			addEventListener(Event.REMOVED_FROM_STAGE, RemovedFromStage);
		}
		private function AddedToStage(e:Event=null):void{
			removeEventListener(Event.ADDED_TO_STAGE, AddedToStage);
			
			if(refireOnContinuedExistance){
				addEventListener(Event.ENTER_FRAME, ContinuedExistance);
			}
			
			findWorld();
			if(hasValue(name)){
				shootByName(name);
			}else{
				shoot(sbgUtil.tank.ammo, sbgUtil.tank.power);
			}
		}
		private function shootByName(bulletName:String):void{
			for(var n:int = 0; n<40; n++){
				bulletName = bulletName.replace("_","");
				if(bulletName.indexOf("_")==1){
					break;
				}
			}
			shoot(getDefinitionByName(bulletName)as Class);
		}
		private function RemovedFromStage(e:Event=null):void{
			world=null;
			body=null;
			pos=null;
			angle=null;
			
			removeEventListener(Event.ADDED_TO_STAGE, AddedToStage);
			removeEventListener(Event.REMOVED_FROM_STAGE, RemovedFromStage);
			removeEventListener(Event.ENTER_FRAME, ContinuedExistance);
		}
		
		private function ContinuedExistance(e:Event=null):void{
			if(hasValue(name)){
				shootByName(name);
			}else{
				shoot(sbgUtil.tank.ammo, sbgUtil.tank.power);
			}
		}
		
		private function findWorld():void{
			var par:DisplayObject = this;
			for(var i:int = 0; i<=worldFathoms; i++){
				_rotationToWorld+=par.rotation;
				
				world = par.parent as wck.World;
				
				if(world){
					spawnDepth = world.getChildIndex(par);
					break;
				}
				par = par.parent;
			}
		}
		private function findBody():void{
			var par:DisplayObject = this;
			for(var i:int = 0; i<=worldFathoms; i++){
				body = par.parent as BodyShape;
				if(body){
					body = body.body;//make sure its not just the shape.
					break;
				}
				par = par.parent;
			}
		}
		
		private var globalMatrix:Matrix;
		private var skew:Point = new Point();
		private var startDirection:Point = new Point(0, -1);//up
		private var parRotation:Number;
		private var isBullet:Boolean = false;
		private function shoot(bodyclass:Class, power:Number=0):void{
			//find registration point relative to world.
			pos.x = this.x;
			pos.y = this.y;
			pos = parent.localToGlobal(pos);
			pos = world.globalToLocal(pos);
			
			if(MovieClip(parent).name == "Turret"){
				//trace("canon ball");
				//find rotation relative to world.
				globalMatrix = this.transform.concatenatedMatrix
				angle = globalMatrix.deltaTransformPoint( new Point(0 -1) );
				skew.x = MatrixTransformer.getSkewX(globalMatrix);
				skew.y = MatrixTransformer.getSkewY(globalMatrix);
				
				parRotation = Math.atan2(angle.y, angle.x) * 180 / Math.PI;
				
				if(int(skew.x) != int(skew.y)){//flipped//Math.abs(skew.x - skew.y) == 180
					//trace(parent.name+" : "+skew);
					parRotation += 180;
				}
				parRotation+=180;
				isBullet=true;
			}else{
				//trace("explosion");
				//just use local rotation and skip the math.
				parRotation = rotation + parent.rotation + parent.parent.rotation;
				isBullet=false;
			}
			
			
			//up = 0;
			var rot:Number = parRotation -90;
			//accuracy reduction by arrow scaleX
			//rot += (Math.random()*scaleX*2 - scaleX)
			
			//find unit vector of rotation.
			angle.x = Math.cos(rot * Math.PI/180);
			angle.y = Math.sin(rot * Math.PI/180);
			
			//make bullet
			var bullet:BodyShape = new bodyclass();
			bullet.x = pos.x;
			bullet.y = pos.y;
			bullet.bullet=isBullet;
			//bullet.reportBeginContact=true;
			
			if(power == 0){
				var power:Number = scaleY*8;
			}
			bullet.linearVelocityX = power*angle.x;
			bullet.linearVelocityY = power*angle.y;
			
			if(addBodyVelocity){
				findBody();
				if(body){
					bullet.linearVelocityX += body.b2body.GetLinearVelocity().x;
					bullet.linearVelocityY += body.b2body.GetLinearVelocity().y;
				}
			}
			
			world.addChildAt(bullet,spawnDepth);
		}
		public static function hasValue(val:*):Boolean{
			if ( val.indexOf("instance")>=0 ) return false;
			
			return true;
		}
	}
}

/*
*__room for improvement__
*-implemented all the features i thought of, lastly the 2 options.  perhaps they should be public static to be easily settable, but that would effect all shoot arrows. probably a common desire, but not universal..
*-better examples of use.
*/